using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Media.Imaging;

namespace 修为尽失.辅助.exts
{
    public enum SnapDirection
    {
        Left,
        Right
    }
    public static class SetExtension
    {
        public static Bitmap ToBitmap(this byte[] data)
        {
            Bitmap bitmap = null;
            try
            {
                var ms = new MemoryStream(data);
                bitmap = new Bitmap(ms);
                ms.Close();
            }
            catch (Exception ex)
            {
               // MessageManager.Send(new Message(ex.ToLogString(), MessageType.错误));
                return null;
            }

            return bitmap;
        }

        public static BitmapSource ToBitmapSource(this byte[] data)
        {
            try
            {
                var bi = new BitmapImage();
                bi.BeginInit();
                var ms = new MemoryStream(data);
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
                ms.Close();
                return bi;
            }
            catch (Exception ex)
            {
               // MessageManager.Send(new Message(ex.ToLogString(), MessageType.错误));
                return null;
            }
        }

        public static void AddRange<T>(this ICollection<T> @this, IEnumerable<T> values)
        {
            foreach (var obj in values)
            {
                try
                {
                    @this.Add(obj);
                }
                catch
                {
                    continue;
                }
            }
        }

        public static IEnumerable<Ttarget> ConvertAll<Ttarget>(this IEnumerable @this) where Ttarget : class
        {
            if (@this == null)
                return (IEnumerable<Ttarget>) null;
            var targets = @this as IEnumerable<Ttarget>;
            if (targets != null)
                return targets;
            var collection = new Collection<Ttarget>();
            foreach (var thi in @this)
            {
                var target = thi as Ttarget;
                if (target == null)
                    throw new ArgumentException("invalid Target:" + ((object) typeof(Ttarget)));
                collection.Add(target);
            }

            return (IEnumerable<Ttarget>) collection;
        }

        public static IEnumerable<Ttarget> ConvertAllyield<Ttarget>(this IEnumerable @this) where Ttarget : class
        {
            if (@this != null)
            {
                foreach (var thi in @this)
                {
                    var titem = thi as Ttarget;
                    yield return titem;
                }
            }
        }

        public static T FindFirst<T>(this IEnumerable<T> source) where T : class
        {
            if (source.IsInvalid<T>())
                return default(T);
            using (var enumerator = source.GetEnumerator())
            {
                if (enumerator.MoveNext())
                    return enumerator.Current;
            }

            return default(T);
        }

        public static object FindFirst(this IEnumerable source)
        {
            if (source.IsInvalid())
                return (object) null;
            var enumerator = source.GetEnumerator();
            try
            {
                if (enumerator.MoveNext())
                    return enumerator.Current;
            }
            finally
            {
                var disposable = enumerator as IDisposable;
                if (disposable != null)
                    disposable.Dispose();
            }

            return (object) null;
        }

        public static void ForEach<T>(this IEnumerable<T> souce, Action<T> action)
        {
            if (souce.IsInvalid<T>())
                return;
            foreach (var obj in souce)
                action(obj);
        }

        public static TValue GetSafeValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue defalut)
        {
            if (dictionary.IsInvalid() || (!dictionary.ContainsKey(key)))
                return defalut;
            return dictionary[key];
        }

        public static T GetSafeValue<T>(this IList<T> @this, int index, T defaultValue)
        {
            if (@this.IsInvalid<T>() || (@this.Count > index))
                return defaultValue;
            return @this[index];
        }

        public static string GetString(this byte[] bytes) => bytes.GetString(Encoding.Default);

        public static string GetString(this byte[] bytes, Encoding encode)
        {
            if ((bytes == null) || (bytes.Count() <= 0))
                return string.Empty;
            return encode.GetString(bytes);
        }

        public static bool IsInvalid<T>(this IEnumerable<T> source) => !source.IsValid<T>();
        public static bool IsInvalid(this IEnumerable @this) => !@this.IsValid();
        public static bool IsValid<T>(this IEnumerable<T> source) => (source != null) && source.Any<T>();

        public static bool IsValid(this IEnumerable @this)
        {
            if (@this == null)
                return false;
            var enumerator = @this.GetEnumerator();
            try
            {
                if (enumerator.MoveNext())
                {
                    var current = enumerator.Current;
                    return true;
                }
            }
            finally
            {
                var disposable = enumerator as IDisposable;
                if (disposable != null)
                    disposable.Dispose();
            }

            return false;
        }

        public static bool IsValid(this string value) => !string.IsNullOrWhiteSpace(value);

        /// <summary>
        /// 字符串相似度算法
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public static int LevenshteinDistance(this string str1, string str2)
        {
            var scratchDistanceMatrix = new int[str1.Length + 1, str2.Length + 1];
            // distance matrix contains one extra row and column for the seed values         
            for (var i = 0; i <= str1.Length; i++)
            {
                scratchDistanceMatrix[i, 0] = i;
            }

            for (var j = 0; j <= str2.Length; j++)
            {
                scratchDistanceMatrix[0, j] = j;
            }

            for (var i = 1; i <= str1.Length; i++)
            {
                var str1Index = i - 1;
                for (var j = 1; j <= str2.Length; j++)
                {
                    var str2Index = j - 1;
                    var cost = (str1[str1Index] == str2[str2Index]) ? 0 : 1;
                    var deletion = (i           == 0) ? 1 : (scratchDistanceMatrix[i            - 1, j]     + 1);
                    var insertion = (j          == 0) ? 1 : (scratchDistanceMatrix[i, j         - 1]        + 1);
                    var substitution = ((i == 0) || (j == 0)) ? cost : (scratchDistanceMatrix[i - 1, j - 1] + cost);
                    scratchDistanceMatrix[i, j] = Math.Min(Math.Min(deletion, insertion), substitution);
                    // Check for Transposition  
                    if ((i > 1) && (j > 1) && (str1[str1Index] == str2[str2Index - 1]) && (str1[str1Index - 1] == str2[str2Index]))
                    {
                        scratchDistanceMatrix[i, j] = Math.Min(scratchDistanceMatrix[i, j], scratchDistanceMatrix[i - 2, j - 2] + cost);
                    }
                }
            }

            // Levenshtein distance is the bottom right element       
            return scratchDistanceMatrix[str1.Length, str2.Length];
        }

        public static Match Match(this string source, string pattern) => source.Match(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
        public static Match Match(this string source, string pattern, RegexOptions options) => Regex.Match(source, pattern, options);

        /// <summary>
        /// 标准差
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static double StandardDeviation(this IEnumerable<double> i) => Math.Sqrt(i.Variance());

        /// <summary>
        /// 标准差
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static double StandardDeviation(this IEnumerable<int> i) => Math.Sqrt(i.Variance());

        /// <summary>
        /// 平方差
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static double Variance(this IEnumerable<int> i)
        {
            var s = 0d;
            var c = 0d;
            foreach (var item in i)
            {
                s += item;
                c++;
            }

            var a = s / c;
            var v = 0d;
            foreach (var item in i)
            {
                v += Math.Pow(item - a, 2);
            }

            return v / (c - 1);
        }

        /// <summary>
        /// 平方差
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static double Variance(this IEnumerable<double> i)
        {
            var s = 0d;
            var c = 0d;
            foreach (var item in i)
            {
                s += item;
                c++;
            }

            var a = s / c;
            var v = 0d;
            foreach (var item in i)
            {
                v += Math.Pow(item - a, 2);
            }

            return v / (c - 1);
        }

        public static string ToBinary(this byte[] values, string separator = " ")
        {
            if ((values == null) || (values.Length <= 0))
                return string.Empty;
            var stringBuilder = new StringBuilder();
            foreach (var num in values)
                stringBuilder.Append(num.ToBinary()).Append(separator);
            return stringBuilder.ToString();
        }

        public static List<T> ToList<T>(this IEnumerator<T> source)
        {
            var objList = new List<T>();
            if (source == null)
                return objList;
            while (source.MoveNext())
                objList.Add(source.Current);
            return objList;
        }

        public static string ToPrintableString<O>(this IEnumerable<O> t, int printlength, int wraplength = 5, string wrapper = "\n", string itemseparator = "\t", bool snap = false, SnapDirection direction = SnapDirection.Left)
        {
            if (!t.Any())
                return string.Empty;
            if (printlength > t.Count())
                printlength = t.Count();
            if (wraplength > printlength)
                wraplength = printlength;
            var sb = new StringBuilder();
            var i = 0;
            var max = 0;
            if (snap)
            {
                max = t.Take(printlength).Max(a =>
                {
                    if (a == null)
                        return 0;
                    return a.ToString().GetWidth();
                });
            }

            foreach (var item in t)
            {
                string ext = string.Empty;
                if (snap)
                {
                    if (item == null)
                        ext = " ".X(max);
                    else
                        ext = " ".X(max - item.ToString().GetWidth());
                }

                if (i < printlength)
                {
                    if (i != 0 && i % wraplength == 0)
                        sb.Append(wrapper);
                    var s = "--";
                    if (item != null)
                        s = item.ToString();
                    if (direction == SnapDirection.Left)
                        sb.Append($"{s}{ext}");
                    else
                        sb.Append($"{ext}{s}");
                    sb.Append(itemseparator);
                }
                else
                    break;

                i++;
            }

            return sb.ToString().TrimEnd(itemseparator);
        }

        public static string ToPrintableString<O>(this IEnumerable<O> t, int wraplength = 5, string wrapper = "\n", string itemseparator = "\t", bool snap = false, SnapDirection direction = SnapDirection.Left) => ToPrintableString(t, t.Count(), wraplength, wrapper, itemseparator, snap, direction);

        public static string ToPrintableString<K, V>(this IDictionary<K, V> t, int printlength, int wraplength = 5, string wrapper = "\n", string itemseparator = "\t", bool snap = false, SnapDirection direction = SnapDirection.Left)
        {
            if (t.Count == 0)
                return string.Empty;
            if (printlength > t.Count)
                printlength = t.Count;
            if (wraplength > printlength)
                wraplength = printlength;
            var sb = new StringBuilder();
            var i = 0;
            var max = 0;
            if (snap)
            {
                max = t.Take(printlength).Max(a => a.ToString().GetWidth());
            }

            foreach (var item in t)
            {
                string ext = string.Empty;
                var s = item.Key + ":" + item.Value;
                if (snap)
                    ext = " ".X(max - s.GetWidth());
                if (i < printlength)
                {
                    if (i != 0 && i % wraplength == 0)
                        sb.Append(wrapper);
                    if (direction == SnapDirection.Left)
                        sb.Append($"{s}{ext}");
                    else
                        sb.Append($"{ext}{s}");
                    sb.Append(itemseparator);
                }
                else
                    break;

                i++;
            }

            return sb.ToString().TrimEnd(itemseparator);
        }

        public static string ToPrintableString<K, V>(this IDictionary<K, V> t, int wraplength = 5, string wrapper = "\n", string itemseparator = "\t", bool snap = false, SnapDirection direction = SnapDirection.Left) => ToPrintableString(t, t.Count, wraplength, wrapper, itemseparator, snap, direction);
    }
}